package com.myapp.web.tomcat;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.catalina.comet.CometEvent;
import org.apache.catalina.comet.CometProcessor;
import com.myapp.web.struts2.multipart.FileItemProgressInfo;
import com.myapp.web.struts2.multipart.FileUploadListener;
import com.myapp.web.struts2.multipart.ProgressChangeEvent;
import com.myapp.web.struts2.multipart.ProgressChangeListener;
import com.myapp.web.struts2.multipart.ProgressMultiPartRequest;
import com.opensymphony.xwork2.util.logging.Logger;
import com.opensymphony.xwork2.util.logging.LoggerFactory;

/*
 * In Tomcat replace the HTTP/1.1 protocol with the following:
   <Connector connectionTimeout="20000" port="8080" 
	protocol="org.apache.coyote.http11.Http11NioProtocol" redirectPort="8443"/>
 **/
public class ProgressServlet extends HttpServlet implements CometProcessor,ProgressChangeListener{
	private static final long serialVersionUID = -1495636100197958999L;
	private HttpServletRequest  request ;
	private HttpServletResponse response;
	static final Logger LOG = LoggerFactory.getLogger(ProgressServlet.class);
	private BlockingQueue<ProgressChangeEvent> eventQueue = new LinkedBlockingQueue<ProgressChangeEvent>();
	
	private ProgressWoker worker = null;
	private boolean bRegistered = false;
	
	public ProgressServlet(){
	}
	
	@Override
	public void init() throws ServletException {
		super.init();
		worker = new ProgressWoker();
		ExecutorService executor = Executors.newSingleThreadExecutor();
		executor.execute(worker);
	}
	
	public void destroy() {
		eventQueue.clear();
        if(worker != null){
            worker.stop();
        }
        worker = null;
    }

	protected void registerListener(HttpServletRequest request){
		if(request != null){
			FileUploadListener listener = (FileUploadListener)request.getSession().getAttribute(ProgressMultiPartRequest.PROGRESS_KEY);
			if(listener != null){
				listener.addEventListener(this);
				LOG.debug("the progress listener registered.");
				bRegistered = true;
			}else{
				bRegistered = false;
				LOG.warn("the progress listener don't exists.");
			}
		}else{
			LOG.warn("unexpected exception when registering listener.");
		}		
	}
	
	protected void updateProcess(HttpServletRequest request,HttpServletResponse response,FileItemProgressInfo info){		
		
		float progress = (float)(info.getBytesRead())/info.getTotalSize() * 100.00f; 
		if(request == null || request.getSession() == null){
			LOG.warn("the request is null or session is null.");
			return ;
		}
		System.out.println("Comet session Id : " + request.getSession().getId());
		
		try {
			response.setHeader("Pragma","No-cache"); 
			response.setHeader("Cache-Control","no-cache"); 
			response.setDateHeader("Expires", 0); 
			PrintWriter writer = response.getWriter();
			writer.println("{'progress':" + progress + "," + "'time':"+ System.currentTimeMillis() + '}');
			writer.flush();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	@Override
	public void event(CometEvent event) throws IOException, ServletException {		
		if(event.getEventType() == CometEvent.EventType.BEGIN){
			event.setTimeout(30 * 1000);
			request  = event.getHttpServletRequest();
			response = event.getHttpServletResponse();
			registerListener(request);
			LOG.debug("comet servelt ................... begin..........");
		}else if(event.getEventType() == CometEvent.EventType.READ){
			LOG.debug("comet servelt ................... read..........");
			//readProcess();
		}else if(event.getEventType() == CometEvent.EventType.END){
			LOG.debug("comet servelt ................... end..........");
			event.close();
		}else if(event.getEventType() == CometEvent.EventType.ERROR){
			if(event.getEventSubType() != null){
				LOG.warn(event.getEventSubType().toString());
			}
			event.close();
		}		
	}
	
	public class ProgressWoker implements Runnable {
		
		protected boolean running = true;
				
		public void stop(){
			running = false;
		}
		
		@Override
		public void run(){
			while(running){
				try {
					if(!bRegistered){
						LOG.debug("the progress listener did't register, need to try again.");
						ProgressServlet.this.registerListener(ProgressServlet.this.request);
					}
					if(!bRegistered){
						Thread.sleep(3);
						continue;
					}
					LOG.debug("thread running..............");
					ProgressChangeEvent event =  eventQueue.take();
					FileItemProgressInfo info = (FileItemProgressInfo)event.getSource();
					if(info != null ){
						updateProcess(request,response,info);
					}
					/*if(info.isCompleted()){
						response.getWriter().close();
					}*/
				} catch (InterruptedException e) {
					e.printStackTrace();
				}			
			}
		}		
	}

	@Override
	public void progressChange(ProgressChangeEvent evt){		
		if(!eventQueue.isEmpty() && eventQueue.contains(evt)){
			return ;
		}
		eventQueue.add(evt);
	}
}
